Anton Lydike — Blog
Website GitHub

STM Board Dev Setup on NixOS

Written: 2025-08-11
Tags: #arm #nixos

After trying to get stm32CubeIDE working on NixOS and failing at every possible step, I decided to instead use VSCode+Plugins to work. This may or may not be a good idea.

Installing STM32CubeCLT

Attempt 1:

  1. Download it from their website
  2. Extract the zip unzip <name>.zip
  3. Run the installer steam-run <name>
  4. It fails when it tries to install something into /etc/...

Attempt 2:

With the help of this little script, we can extract these self-extracting-scripts made with Makeself (requires fish and ripgrep):

function mkself-extract
    if test (count $argv) -ne 2
        echo "Usage: mkself-extract <file> <dest>" >&2
        return 1
    end

    set -l file $argv[1]
    set -l dest $argv[2]

    # Get byte offset where the string starts (first match)
    set -l offset (LC_ALL=C rg --byte-offset --text '\./(?-u:\x00)' $file | head -n1 | cut -d: -f1)

    if test -z "$offset"
        echo "String not found in file" >&2
        return 2
    end

    # create dest folder
    if ! mkdir $dest
        echo "$dest already exists!" >&2
        return 3
    end

    # Print everything after the string
    tail -c +(math 1 + $offset) "$file" | tar -xf - -C $dest
end

We can use this by running mkself-extract st-stm32cubeclt_1.19.0_25876_20250729_1159_amd64.sh stm32cubectl-install. This will create and populate the stm32cubectl-install folder. This will contain the stm32cubectl installer data. All we need from there is the tar.gz archive, which we can extract like this: tar -xcf stm32cubectl-install/st-stm32cubeclt_1.19.0_25876_20250729_1159_amd64.tar.gz -C st-stm32cubeclt_1.19.0.

We now have a functional folder with binaries that work out-of-the-box on NixOS!

Installing CubeMX:

Installing CubeMX on NixOS is a lost cause as it includes too much garbage like bundled browsers that are not in binaries that can be patched for some reason. It's super flakey, and doesn't even work properly through steam-run.

Instead, I recommend running it in podman with X passthrough: (assuming that your st-related stuff including CubeMX is located in ~/st)

$ podman run -it --rm -e DISPLAY -v ~/.Xauthority:/root/.Xauthority:Z -v ~/st:/st:Z -v ~/.stm32cubemx/:/root/.stm32cubemx:Z -v ~/STM32Cube/:/root/STM32Cube/ --net=host ubuntu
$ apt update && apt install -y xorg && ~/st/STM32CubeMX/STM32CubeMX

The udev rules are included alongside the CubeCTL installer and can be extracted by running mkself-extract stm32cubectl-installer/st-stlink-udev-rules-1.0.3-2-linux-noarch.sh udevrules, which will then contain a few udev rules. Installing these works through supplying a special package to services.udev.packages. We'll get to this later, because:

stlink-server is a special binary that needs to be root owned. I will prepare a small nix package that contains these1:

{ pkgs ? import <nixpkgs> { system = builtins.currentSystem; }
, stdenv ? pkgs.stdenv
, lib ? pkgs.lib
}:
stdenv.mkDerivation rec {
  pname = "stm32dev";
  version = "1.18.0";

  doNotUnpack = true;
  src = ./.;

  nativeBuildInputs = [
    pkgs.autoPatchelfHook
  ];

  buildInputs = with pkgs; [
      libusb1
  ];

  installPhase = ''
    runHook preInstall

    # install the stlink-server binary as root
    patchelf bin/stlink-server
    install -D -t $out/bin/ bin/stlink-server
    # install udev rules
    install -D -t $out/etc/udev/rules.d/ udev/*.rules

    runHook postInstall
  '';

  meta = with lib; {
    homepage = "https://www.st.com/en/development-tools/stm32cubeide.html";
    description = "STM32 Misc Dependencies (udev rules and stlink-server)";
    platforms = platforms.linux;
  };
  # include using (callPackage /home/<user>/path/to/this/file.nix {})
}

This file should be located in a folder that also contains the extracted udev rules in a subfolder udev and an unmodified stlink-server binary. Here is my folder layout:

nix-pkg
├── bin
│   └── stlink-server
├── st-dev.nix
└── udev
    ├── 49-stlinkv1.rules
    ├── 49-stlinkv2-1.rules
    ├── 49-stlinkv2.rules
    └── 49-stlinkv3.rules

This then needs to be installed as a package, as well as added to the udev rules:

  # ...
  # List packages installed in system profile.
  environment.systemPackages = with pkgs; [
    # ...
    # my stm32cube stuff:
    (pkgs.callPackage /home/anton/st/nix-pkg/st-dev.nix {})
  ];

  # also install the udev rules
  services.udev.packages = [
    (pkgs.callPackage /home/anton/st/nix-pkg/st-dev.nix {}) 
  ];
  # ...

  1. I am actually not sure if this needs to be globally installed anymore, but it doesn't hurt to try? Anyway...